/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 */

#include <rtdevice.h>

/**@brief Function for initializing the FIFO.
 *
 * @param[out] fifo FIFO object.
 * @param[in]  pool FIFO data for storing data. The data size must be a power of two.
 * @param[in]  size Size of the FIFO data provided. This size must be a power of two.
 */
void rt_fifo_init(rt_fifo_t *fifo, rt_uint8_t *pool, rt_uint32_t size)
{
    RT_ASSERT(fifo != RT_NULL);
    RT_ASSERT(size > 0);
    RT_ASSERT(!(size & (size - 1)));

    fifo->buffer_ptr       = pool;
    fifo->buffer_size_mask = size - 1;
    fifo->read_index       = 0;
    fifo->write_index      = 0;
}

rt_uint32_t rt_fifo_put(rt_fifo_t *fifo, const rt_uint8_t byte)
{
    if (!rt_fifo_full(fifo))
    {
        fifo->buffer_ptr[fifo->write_index++ & fifo->buffer_size_mask] = byte;
        return 1;
    }

    return 0;
}

rt_uint32_t rt_fifo_get(rt_fifo_t *fifo, rt_uint8_t *byte)
{
    if (!rt_fifo_empty(fifo))
    {
        *byte = fifo->buffer_ptr[fifo->read_index++ & fifo->buffer_size_mask];
        return 1;
    }

    return 0;
}

void rt_fifo_flush(rt_fifo_t *fifo)
{
    fifo->read_index = fifo->write_index;
}

void rt_fifo_clear(rt_fifo_t *fifo)
{
    fifo->read_index = fifo->write_index = 0;
}

rt_uint32_t rt_fifo_read(rt_fifo_t *fifo, rt_uint8_t *ptr, rt_uint32_t length)
{
    rt_uint32_t byte_count = rt_fifo_length(fifo);
    rt_uint32_t offset, remain;

    RT_ASSERT(ptr != RT_NULL);

    if (byte_count == 0)
        return 0;

    if (length > byte_count)
        length = byte_count;

    offset = fifo->read_index & fifo->buffer_size_mask;

    remain = fifo->buffer_size_mask + 1 - offset;
    remain = remain > length ? length : remain;

    rt_memcpy(ptr, ((uint8_t *)(fifo->buffer_ptr)) + offset, remain);
    if (length > remain)
        rt_memcpy((uint8_t *)ptr + remain, fifo->buffer_ptr, length - remain);

    fifo->read_index += length;

    return length;
}

rt_uint32_t rt_fifo_write(rt_fifo_t *fifo, const rt_uint8_t *ptr, rt_uint32_t length)
{
    rt_uint32_t free_length = rt_fifo_free_length(fifo);
    rt_uint32_t offset, remain;

    RT_ASSERT(ptr != RT_NULL);

    if (free_length == 0)
        return 0;

    if (length > free_length)
        length = free_length;

    offset = fifo->write_index & fifo->buffer_size_mask;

    remain = fifo->buffer_size_mask + 1 - offset;
    remain = remain > length ? length : remain;

    rt_memcpy(((uint8_t *)(fifo->buffer_ptr)) + offset, ptr, remain);
    if (length > remain)
        rt_memcpy(fifo->buffer_ptr, (uint8_t *)ptr + remain, length - remain);

    fifo->write_index += length;

    return length;
}

rt_uint32_t rt_fifo_get_read_ptr(rt_fifo_t *fifo, rt_uint8_t **ptr)
{
    rt_uint32_t byte_count = rt_fifo_length(fifo);
    rt_uint32_t offset, remain;

    RT_ASSERT(ptr != RT_NULL);

    if (byte_count == 0)
    {
        *ptr = RT_NULL;
        return 0;
    }

    offset = fifo->read_index & fifo->buffer_size_mask;
    *ptr   = &fifo->buffer_ptr[offset];
    remain = fifo->buffer_size_mask + 1 - offset;
    remain = remain > byte_count ? byte_count : remain;

    return remain;
}

rt_uint32_t rt_fifo_get_write_ptr(rt_fifo_t *fifo, rt_uint8_t **ptr)
{
    rt_uint32_t free_length = rt_fifo_free_length(fifo);
    rt_uint32_t offset, remain;

    RT_ASSERT(ptr != RT_NULL);

    if (free_length == 0)
    {
        *ptr = RT_NULL;
        return 0;
    }

    offset = fifo->write_index & fifo->buffer_size_mask;
    *ptr = &fifo->buffer_ptr[offset];
    remain = fifo->buffer_size_mask + 1 - offset;
    remain = remain > free_length ? free_length : remain;

    return remain;
}

void rt_fifo_write_index_update(rt_fifo_t *fifo, rt_uint32_t idx)
{
    rt_uint32_t free_length = rt_fifo_free_length(fifo);

    if (idx > free_length)
        fifo->read_index += idx - free_length;

    fifo->write_index += idx;
}

void rt_fifo_read_index_update(rt_fifo_t *fifo, rt_uint32_t idx)
{
    rt_uint32_t byte_count = rt_fifo_length(fifo);

    if (idx > byte_count)
        idx = byte_count;

    fifo->read_index += idx;
}
